/*
 *
 */

#include "user_common.h"
#include "user_stash.h"

static void user_authority_threaten_changed(uint32_t *authority)
{
#if defined(GKT_CONFIG_USER_TYPE_THREATEN_ENABLE) && GKT_CONFIG_USER_TYPE_THREATEN_ENABLE
	if (GKT_USER_AUTH_THREATEN == *authority)
		*authority = GKT_USER_AUTH_NORMAL;
#endif
}

int gkt_user_get_amount(gkt_user_amount_s *amount,
				uint32_t authority, uint32_t type)
{
	user_instance_s *instance;

	user_authority_threaten_changed(&authority);

	if (amount) {
		amount->used_nums = 0;
		amount->max_nums = 0;

		if ((type < GKT_USER_TYPE_MAX)
			&& (authority < GKT_USER_AUTH_TEMPORARY))
		{
			instance = user_get_instance(type);
			if (instance
				&& (instance->flags & USER_INSTANCE_F_INIT_OK)) 
			{
				amount->used_nums = instance->info->auth_user_used_nums[authority];
				amount->max_nums = instance->auth_user_maxnums[authority];
			}
		}
		else if ((type < GKT_USER_TYPE_MAX)
			&& (authority >= GKT_USER_AUTH_MAX))
		{
			instance = user_get_instance(type);
			if (instance
				&& (instance->flags & USER_INSTANCE_F_INIT_OK)) 
			{
				amount->used_nums += instance->info->user_used_nums;
				amount->max_nums = instance->user_maxnums;
			}
		}
		else if ((type >= GKT_USER_TYPE_MAX)
			&& (authority < GKT_USER_AUTH_TEMPORARY))
		{
			for (type = 0; type < GKT_USER_TYPE_MAX; type++) {
				instance = user_get_instance(type);
				if (instance
					&& (instance->flags & USER_INSTANCE_F_INIT_OK)) 
				{
					amount->used_nums += instance->info->auth_user_used_nums[authority];
					amount->max_nums += instance->auth_user_maxnums[authority];
				}
			}
		}
		else if ((type >= GKT_USER_TYPE_MAX)
			&& (authority >= GKT_USER_AUTH_MAX))
		{
			for (type = 0; type < GKT_USER_TYPE_MAX; type++) {
				instance = user_get_instance(type);
				if (instance
					&& (instance->flags & USER_INSTANCE_F_INIT_OK))
				{
					amount->used_nums += instance->info->user_used_nums;
					amount->max_nums = instance->user_maxnums;
				}
			}
		}

		return GKT_SUCCESS;
	}

	return GKT_EPARAM;
}

int gkt_user_verify(uint32_t *user_id, 
				uint32_t type, uint32_t authority, 
				const void *data, uint32_t length)
{
	user_instance_s *instance;
	uint32_t excluded_user_id, excluded_user_index;
	int retval;

	gkt_trace("gkt_user_verify: type(%u), auth(%u), len(%u)\n", 
		type, authority, length);

	user_authority_threaten_changed(&authority);

	if (user_id
		&& (type < GKT_USER_TYPE_MAX)
		&& data && (length > 0)) 
	{
		instance = user_get_instance(type);
		if (!instance) {
			gkt_error("gkt_user_verify: type(%u) not support\n", type);
			return GKT_ENOTSUPPORT;
		}
		else if (!(instance->flags & USER_INSTANCE_F_INIT_OK)) {
			gkt_error("gkt_user_verify: NOT INIT OK!\n");
			return GKT_ENOTINIT;
		}
		else if (instance->verify_item_index < instance->item_available_nums) {
			gkt_trace("gkt_user_verify: finalize previous verify!\n");
			user_verify_finalize(instance);
		}

		excluded_user_id = *user_id;
		if ((GKT_USER_ID_TAG == GKT_USER_TAG(excluded_user_id))
			&& (GKT_USER_TYPE(excluded_user_id) == type))
		{
			excluded_user_index = GKT_USER_INDEX(excluded_user_id);
		}
		else
			excluded_user_index = ~0;
		retval = user_verify(instance, user_id, excluded_user_index, 
						authority, data, length);
		if (retval != GKT_USER_VERIFY_PASSED_UPDATE)
			user_verify_finalize(instance);
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

void gkt_user_verify_update(uint32_t type)
{
	user_instance_s *instance;
	int retval;

	gkt_trace("gkt_user_verify_update: type(%u)\n", type);

	if (type < GKT_USER_TYPE_MAX) {
		instance = user_get_instance(type);
		if (instance 
			&& (instance->flags & USER_INSTANCE_F_INIT_OK)
			&& (instance->flags & USER_INSTANCE_F_ITEM_UPDATE_ENABLE))
		{
			if (instance->verify_item_index >= instance->item_available_nums) {
				gkt_info("gkt_user_verify_update: verify_item_index invalid!\n");
				return;
			}

			retval = user_reload_items_v(instance);
			if (GKT_SUCCESS == retval)
				user_verify_update(instance);
			user_verify_finalize(instance);
		}
		else if(instance 
			&& (instance->flags & USER_INSTANCE_F_INIT_OK)
			&& (instance->flags & USER_INSTANCE_F_ITEM_UPDATE_DATA_ONLY_ENABLE)){
			//finger update no need update local data,just update ex_data
			if(instance->c_ops->verify_update)
				(*instance->c_ops->verify_update)();
		}
	}
	else {
		gkt_warning("gkt_user_verify_update: un-support type(%u)\n", type);
	}
}

uint32_t gkt_user_get_id(uint32_t user_index)
{
	user_instance_s *instance;
	const user_item_header_u *c_item_header;
	uint32_t user_id, type;

	user_id = 0;
	if ((user_index >= GKT_USER_INDEX_MIN)
		&& (user_index <= GKT_USER_INDEX_MAX))
	{
		type = user_get_type(user_index);
		if (type < GKT_USER_TYPE_MAX) {
			instance = user_get_instance(type);
			if (instance) {
				c_item_header = user_find_item(instance, user_index, NULL);
				if (c_item_header) {
					user_id = GKT_USER_ID_BUILD(c_item_header->authority.type, 
									c_item_header->authority.threaten,
									type, user_index);
				}
			}
		}
	}

	return user_id;
}

uint32_t gkt_user_get_free_index(uint32_t authority)
{
	user_authority_threaten_changed(&authority);
	return (authority < GKT_USER_AUTH_TEMPORARY) ?
				user_get_free_index(authority) : ~0;
}

int gkt_user_enroll_init(uint32_t type, uint32_t authority, 
			uint32_t threaten, uint32_t user_index)
{
	user_instance_s *instance;
	int retval;

	gkt_trace("gkt_user_enroll_init: type(%u), auth(%u), user_index(%u)\n",
				type, authority, user_index);

	user_authority_threaten_changed(&authority);
	if ((authority < GKT_USER_AUTH_TEMPORARY)
		&& (type < GKT_USER_TYPE_MAX)
		&& (user_index >= GKT_USER_INDEX_MIN)
		&& (user_index <= GKT_USER_INDEX_MAX))
	{
		/* checks whether the specified user_index already exist? */
		if (user_get_type(user_index) < GKT_USER_TYPE_MAX) {
			gkt_trace("gkt_user_enroll_init: user_index already exists!\n");
			return GKT_EALREADYEXIST;
		}

		instance = user_get_instance(type);
		if (!instance) {
			gkt_error("gkt_user_enroll_init: type(%u) not support!\n", type);
			return GKT_ENOTSUPPORT;
		}
		else if (!(instance->flags & USER_INSTANCE_F_INIT_OK)) {
			gkt_error("gkt_user_enroll_init: NOT INIT OK!\n");
			return GKT_ENOTINIT;
		}
		else if (instance->info->auth_user_used_nums[authority] >= 
			instance->auth_user_maxnums[authority])
		{
			gkt_info("gkt_user_enroll_init: (%u) authority(%u) user is FULL!\n",
						instance->type, authority);
			return GKT_ENORESOURCE;
		}
		else if (instance->enroll_item_index < instance->item_available_nums) {
			gkt_warning("gkt_user_enroll_init: previous enroll(%u - %u) not complete!\n",
					instance->item_header->user_index,
					instance->item_header->authority.type);
			user_enroll_uninit(instance);
		}

		retval = user_enroll_init(instance, authority, threaten, user_index);
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_user_enroll(uint32_t type, 
				const void *data, uint32_t length)
{
	user_instance_s *instance;
	int retval;

	gkt_trace("gkt_user_enroll: type(%u), len(%u)\n", type, length);

	if ((type < GKT_USER_TYPE_MAX) 
		&& data && (length > 0)) 
	{
		instance = user_get_instance(type);
		if (!instance) {
			gkt_error("gkt_user_enroll: type(%u) not support!\n", type);
			return GKT_EFATAL;
		}
		else if (instance->enroll_item_index >= instance->item_available_nums) {
			gkt_error("gkt_user_enroll: not enroll_init!\n");
			return GKT_EPERMIT;
		}

		retval = user_enroll_run(instance, data, length);
		if ((GKT_USER_ENROLL_COMPLETE == retval)
			|| (retval >= GKT_USER_ENROLL_UNAVAILABLE)
			|| (retval < GKT_SUCCESS))
		{
			user_enroll_uninit(instance);
		}
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

void gkt_user_delete_index(uint32_t user_index)
{
	user_instance_s *instance;
	uint32_t type;

	gkt_trace("gkt_user_delete_index: user_index(%u)\n", user_index);

	if ((user_index >= GKT_USER_INDEX_MIN) 
		&& (user_index <= GKT_USER_INDEX_MAX))
	{
		type = user_get_type(user_index);
		if (type >= GKT_USER_TYPE_MAX) {
			gkt_info("gkt_user_delete_index: user(%u) not exist!\n", user_index);
			return;
		}

		instance = user_get_instance(type);
		if (instance
			&& (instance->flags & USER_INSTANCE_F_INIT_OK))
		{
			user_delete_index(instance, user_index);
		}
	}
}

void gkt_user_delete_group(uint32_t authority, uint32_t type)
{
	user_instance_s *instance;

	gkt_trace("gkt_user_delete_group: type(%u), auth(%u)\n",
				type, authority);

	if (GKT_USER_AUTH_TEMPORARY == authority)
		return;
	user_authority_threaten_changed(&authority);

	if (type < GKT_USER_TYPE_MAX) {
		instance = user_get_instance(type);
		if (instance
			&& (instance->flags & USER_INSTANCE_F_INIT_OK)) 
		{
			if (authority < GKT_USER_AUTH_TEMPORARY)
				user_delete_authority(instance, authority);
			else
				user_reset(instance);
		}
		else {
			gkt_error("gkt_user_delete_group: type(%u) not support!\n", type);
			return;
		}
	}
	else if (authority < GKT_USER_AUTH_TEMPORARY) {
		for (type = 0; type < GKT_USER_TYPE_MAX; type++) {
			instance = user_get_instance(type);
			if (instance
				&& (instance->flags & USER_INSTANCE_F_INIT_OK))
			{
				user_delete_authority(instance, authority);
			}
		}
	}
}

int gkt_user_reset(void)
{
	user_instance_s *instance;
	uint32_t type;

	gkt_info("gkt_user_reset: ...\n");

	user_reset_common_info();
	user_stash_reset();

	/* reset type */
	for (type = 0; type < GKT_USER_TYPE_MAX; type++) {
		instance = user_get_instance(type);
		if (instance
			&& (instance->flags & USER_INSTANCE_F_INIT_OK)) 
		{
			user_reset(instance);
		}
	}

	return GKT_SUCCESS;
}

int gkt_user_init(void)
{
	user_instance_s *instance;
	uint32_t type;

	user_init_common_info();
	user_stash_init();

	for (type = 0; type < GKT_USER_TYPE_MAX; type++) {
		instance = user_get_instance(type);
		if (instance) {
			GKT_ASSERT(type == instance->type);
			instance->type = type;
			user_init(instance);
		}
	}

	return GKT_SUCCESS;
}

int gkt_user_config(gkt_user_cfg_params_s *cfg_params)
{
	user_instance_s *instance;
	uint32_t type;

	if (cfg_params) {
		for (type = 0; type < GKT_USER_TYPE_MAX; type++) {
			instance = user_get_instance(type);
			if (instance 
				&& (instance->flags & USER_INSTANCE_F_INIT_OK)
				&& instance->c_ops->config)
			{
				(*instance->c_ops->config)(cfg_params);
			}
		}
		
		return GKT_SUCCESS;
	}
	
	return GKT_EPARAM;
}

